/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { ts, rule } from 'qvog-dsl';

const SEVERITY = 2;
const DOC_PATH = 'docs/no-misused-new-check.md';
const DESCRIPTION = 'Enforce valid definition of new and constructor.';

export default rule()
    .match((node): node is ts.MethodDeclaration | ts.ConstructSignatureDeclaration | ts.MethodSignature => {
        return ts.isMethodDeclaration(node) ||
            ts.isConstructSignatureDeclaration(node) ||
            ts.isMethodSignature(node);
    })
    .when((node): boolean => {
        if (ts.isMethodDeclaration(node) || ts.isConstructSignatureDeclaration(node)) { // new 函数
            let name: string | undefined = undefined;
            if (node.name) {
                name = node.name.getText();
            } else {
                name = node.getChildren()[0].getText();
            }
            if (name === 'new') {
                return checkNew(node);
            }
        } else if (ts.isMethodSignature(node)) { // constructor 函数
            if (node.name?.getText() === 'constructor') {
                return checkConstructor(node);
            }
        }
        return false;
    })
    .report({
        severity: SEVERITY,
        description: DESCRIPTION,
        docPath: DOC_PATH,
    });


function checkNew(aNode: ts.Node): boolean {
    let nameNode: ts.Node | undefined = undefined;
    let lastNode: ts.Node | undefined = undefined;
    let nameFlag = false;
    for (const child of aNode.getChildren()) {
        if (nameFlag) {
            nameNode = child;
            break;
        }
        if (lastNode !== undefined && lastNode.kind === ts.SyntaxKind.CloseParenToken && child.kind === ts.SyntaxKind.ColonToken) {
            nameFlag = true;
        }
        lastNode = child;
    }
    if (!nameNode) {
        return false;
    }
    const parentClassNode = findParentClassOrInterfaceNode(aNode);
    if (!parentClassNode) {
        return false;
    }
    if (ts.isInterfaceDeclaration(parentClassNode)) { // interface C { new(): C; }
        const interfaceName = parentClassNode.name.getText();
        let name = nameNode.getText();
        if (nameNode.getChildren().length > 0) {
            name = nameNode.getChildren()[0].getText();
        }
        if (interfaceName === name) {
            return true;
        }
    } else if (ts.isClassDeclaration(parentClassNode)) { // class C { new(): C; }
        const className = parentClassNode.name?.getText();
        if (className && className === nameNode.getText()) {
            return true;
        }
    }
    return false;
}

function findParentClassOrInterfaceNode(aNode: ts.Node): ts.Node | undefined {
    let parent = aNode.parent;
    while (parent !== undefined) {
        if (ts.isClassDeclaration(parent) || ts.isInterfaceDeclaration(parent) || ts.isTypeAliasDeclaration(parent)) {
            return parent;
        }

        parent = parent.parent;
    }

    return undefined;
}

// 检查 constructor 函数
function checkConstructor(aNode: ts.Node): boolean {
    const parentClassNode = findParentClassOrInterfaceNode(aNode);
    if (!parentClassNode) {
        return false;
    }

    if (ts.isInterfaceDeclaration(parentClassNode) || ts.isTypeAliasDeclaration(parentClassNode)) {
        return true;
    }
    return false;
}